import colorsys
from Group import GroupBase
from ..Attributes import Parameter

class AttributeGroup(GroupBase):
    _saved_attributes = ['member_values', '_value']
    signals_to_register = ['value_changed']
    def __init__(self, **kwargs):
        self._device = kwargs.get('device')
        self.value_set_by_osc = False
        self.value_set_by_parent = False
        self.edited = False
        self.captured_by = None
        if kwargs.get('osc_address') is None:
            kwargs.setdefault('osc_address', self._saved_class_name)
        if 'deserialize' not in kwargs:
            self.member_values = {}
        super(AttributeGroup, self).__init__(**kwargs)
        
        
        if self.valid:
            self.value = self.get_value_from_attributes()
            
        elif len(self.members) == 1:
            member = self.members[self.members.keys()[0]]
            if hasattr(member, 'edited'):
                self.value = member.value
                self.edited = member.edited
        if self.osc_enabled:
            self.add_osc_handler(callbacks={'set-value':self.on_osc_set_value, 
                                            'current-value':self.on_osc_request_value})
            self.osc_node.send_message(address='current-value', to_master=True)
    
    @property
    def device(self):
        return self._device
    @device.setter
    def device(self, value):
        self._device = value
        self.valid = self.search_for_members()
        if self.valid:
            self.value = self.get_value_from_attributes()
            for member in self.members.values():
                member.connect('value_changed', self.on_attribute_value_changed)
                #print 'group %s connected to attribute %s' % (self, member)
    
    def unlink(self):
        if self.valid:
            for member in self.members.values():
                member.disconnect(callback=self.on_attribute_value_changed)
        super(AttributeGroup, self).unlink()
    
    def search_for_members(self, **kwargs):
        return False
            
    def add_member(self, **kwargs):
        update_values = False
        if len(self.members) == 0:
            update_values = True
        super(AttributeGroup, self).add_member(**kwargs)
        if not self.membership_lock:
            for key, val in kwargs.iteritems():
                self.member_values.update({key:val.value})
        l = self.value
        if type(self.value) != list:
            l = [self.value]
        if update_values and len(self.members) == len(l):
            self.value = self.get_value_from_attributes()
            
            
        
    def del_member(self, key):
        super(AttributeGroup, self).del_member(key)
        if key in self.member_values and not self.membership_lock:
            del self.member_values[key]
    
    def update_user_value(self, user=None):
        if len(self.users):
            if user:
                value = user.member_values[self.id]
                self.captured_by = user.id
                #print 'group ltp=', value
            else:
                if type(self.value) == list:
                    uvals = [[u.member_values[self.id][x] for x in range(len(self.value))] for u in self.users]
                    value = [max(uval[x]) for x, uval in enumerate(uvals)]
                else:
                    value = max([u.member_values[self.id] for u in self.users])
                self.captured_by = None
                #print 'group htp=', value
            self.value_set_by_parent = True
            self.value = value
        
    def send_value_to_osc(self):
        if self.osc_enabled:
            self.osc_node.send_message(address='set-value', value=self.value)
            
    def on_value_change(self, **kwargs):
        if not self.value_set_by_osc and not self.value_set_by_parent:
            self.send_value_to_osc(**kwargs)
        self.value_set_by_osc = False
        self.value_set_by_parent = False
        
    def on_attribute_value_changed(self, **kwargs):
        for member in self.members.itervalues():
            if member.value_set_by_group:
                return
        self.value = self.get_value_from_attributes()
    
    def on_osc_set_value(self, **kwargs):
        value = kwargs.get('values')
        self.value_set_by_osc = True
        self.value = value
        
    def on_osc_request_value(self, **kwargs):
        if self.osc_node.oscMaster:
            self.send_value_to_osc()
        
    def get_values(self):
        d = {'group_value':self.value, 'member_values':{}}
        for key, val in self.members.iteritems():
            if val.captured_by != self.id:
                if getattr(val, 'base_class') == 'Group':
                    d.update({key:val.get_values()})
                else:
                    d.update({key:val.value})
        return d
        
    def get_objects(self, **kwargs):
        d = kwargs.get('obj_dict', {})
        captured_only = kwargs.get('captured_only', False)
        for key in ['DeviceGroup', 'AttributeGroup', 'Attribute']:
            if key not in d:
                d.update({key:{}})
        
        # TODO: this logic can be less dumb but i don't want to think
        if captured_only:
            if self.captured_by is None:
                d['AttributeGroup'].update({self.id:self})
        else:
            d['AttributeGroup'].update({self.id:self})
            
        if 'obj_dict' not in kwargs:
            kwargs['obj_dict'] = d
        for key, val in self.members.iteritems():
            if val.captured_by != self.id:
                if isinstance(val, AttributeGroup):
                    val.get_objects(**kwargs)
                elif val.saved_class_name == 'PatchedAttribute':
                    d['Attribute'].update({key:val})
        return d

class ProxyGroup(AttributeGroup):
    _saved_class_name = 'Proxy'
    def __init__(self, **kwargs):
        self._value = 0
        super(ProxyGroup, self).__init__(**kwargs)
        
    def search_for_members(self, **kwargs):
        groups = kwargs.get('groups')
        for group in groups:
            d = {group.id:group}
            self.add_member(**d)
        super(ProxyGroup, self).search_for_members(**kwargs)
        return True
    def add_member(self, **kwargs):
        super(ProxyGroup, self).add_member(**kwargs)
        if len(kwargs) and not self.membership_lock:
            uval = kwargs[kwargs.keys()[0]].value
            if type(uval) != type(self._value):
                self._value = type(uval)
                if type(uval) == list:
                    self._value = [0] * len(uval)
    @property
    def value(self):
        return self._value
    @value.setter
    def value(self, value):
        self._value = value
        for key in self.member_values.iterkeys():
            self.member_values[key] = value
        for member in self.members.itervalues():
            member.update_user_value(self)
        self.edited = True
        self.on_value_change()
        self.emit('value_changed', value=self.value, attribute=self)
        
class DimmerGroup(AttributeGroup):
    _saved_class_name = 'Dimmer'
    precedence_mode = 'htp'
    def __init__(self, **kwargs):
        self._value = 0
        self.color_group = None
        super(DimmerGroup, self).__init__(**kwargs)
        
    def search_for_members(self, **kwargs):
        valid = False
        if self.device is not None:
            found = None
            for attrib in self.device.Attributes.itervalues():
                if attrib.name == 'Dimmer':
                    found = attrib
            if found:
                self.add_member(dimmer=found)
            else:
                self.color_group = self.device.Groups.get('Color')
            valid = len(self.members) > 0 or self.color_group is not None
        return valid or super(DimmerGroup, self).search_for_members(**kwargs)
            
    @property
    def value(self):
        return self._value
    @value.setter
    def value(self, value):
        if type(value) == list:
            value = value[0]
        self._value = value
        if self.color_group is not None:
            self.color_group.value_set_by_parent = True
            self.color_group.set_hsv(value=value)
            #self.color_group.color = [value] * 3
        else:
            for key in self.member_values.iterkeys():
                self.member_values[key] = value
            for member in self.members.itervalues():
                member.update_user_value()
        self.edited = True
        self.on_value_change()
        self.emit('value_changed', value=self.value, attribute=self)
        
    def get_value_from_attributes(self):
        if self.color_group is not None:
            return self.color_group._hsv[2]
        return self.members.values()[0].value

class ColorGroup(AttributeGroup):
    _saved_class_name = 'Color'
    color_keys = ['red', 'green', 'blue']
    hsv_keys = ['hue', 'sat', 'val']
    all_keys = color_keys + hsv_keys
    value_attribute = 'hsv'
    value_scale = 1.0
    precedence_mode = 'ltp'
    def __init__(self, **kwargs):
        self._rgb = [0, 0, 0]
        self._hsv = [0, 0, 0]
        super(ColorGroup, self).__init__(**kwargs)
        d = dict(zip(self.all_keys, [self.on_osc_set_color] * len(self.all_keys)))        
        self.add_osc_handler(callbacks=d)
        
    def search_for_members(self, **kwargs):
        valid = False
        if self.device is not None:
            for attrib in self.device.Attributes.itervalues():
                if attrib.name.lower() in self.color_keys:
                    self.add_member(**{attrib.name.lower():attrib})
            valid = len(self.members) >= 3
        return valid or super(ColorGroup, self).search_for_members(**kwargs)
        
    def set_hsv(self, **kwargs):
        #hsv = [kwargs.get(key, self._hsv[x]) for x, key in enumerate(self.hsv_keys)]
        #self.color = colorsys.hsv_to_rgb(*hsv)
        self._set_color(**kwargs)
        
    @property
    def hsv(self):
        return dict(zip(self.hsv_keys, self._hsv))
    @hsv.setter
    def hsv(self, value):
        self._set_color(**value)
    
    @property
    def hue(self):
        return self._hsv[0]
    @hue.setter
    def hue(self, value):
        self._set_color(hue=value)
    @property
    def sat(self):
        return self._hsv[1]
    @sat.setter
    def sat(self, value):
        self._set_color(sat=value)
    @property
    def val(self):
        return self._hsv[2]
    @val.setter
    def val(self, value):
        self._set_color(val=value)
        
    @property
    def _value(self):
        return self._rgb
    @_value.setter
    def _value(self, value):
        self._rgb = value
    @property
    def value(self):
        return self.rgb
    @value.setter
    def value(self, value):
        self.rgb = value
    @property
    def rgb(self):
        return self._rgb
    @rgb.setter
    def rgb(self, value):
        self._set_color(rgb=value)
        
    def _set_color(self, **kwargs):
        rgb = kwargs.get('rgb')
        hsv = [kwargs.get(key, self._hsv[x]) for x, key in enumerate(self.hsv_keys)]
        if rgb:
            self._rgb = rgb
            if rgb == [0]*3:
                self._hsv[2] = 0
            else:
                self._hsv = list(colorsys.rgb_to_hsv(*rgb))
        elif hsv != self._hsv:
            self._hsv = hsv
            self._rgb = [x * 255 for x in colorsys.hsv_to_rgb(*hsv)]
            #print 'group hsv = %s, rgb = %s' % (hsv, self._rgb)
        self.set_attributes()
        self.edited = True
        self.on_value_change(**kwargs)
        self.emit('value_changed', value=self.rgb, attribute=self)
        
    def get_value_from_attributes(self):
        return [self.members[key].value for key in self.color_keys]
        
    def set_attributes(self):
#        for x, val in enumerate(self._rgb):
#            attrib = self.members.get(self.color_keys[x])
#            if attrib:
#                self.member_values.update({attrib.id:val})
#                attrib.update_user_value(self)
        for key, val in self.members.iteritems():
            if isinstance(val, GroupBase):
                self.member_values.update({key:self._rgb})
            elif key in self.color_keys:
                self.member_values.update({key:self._rgb[self.color_keys.index(key)]})
            val.update_user_value(self)
    
    def send_value_to_osc(self, **kwargs):
        if self.osc_enabled:
            if len(kwargs):
                for key, val in kwargs.iteritems():
                    self.osc_node.send_message(address=key, value=val)
                return
        super(ColorGroup, self).send_value_to_osc(**kwargs)
    
    def on_osc_set_color(self, **kwargs):
        key = kwargs.get('method')
        value = kwargs.get('values')[0]
        self.value_set_by_osc = True
        self._set_color(**{key:value})
    
    def on_member_value_changed(self, **kwargs):
        attrib = kwargs.get('attribute')
        value = kwargs.get('value')
        self._rgb[self.color_keys.index(attrib.name.lower())] = value
        #print 'group member_value_changed:', self.name, self._value

class PositionGroup(AttributeGroup):
    _saved_class_name = 'Position'
    pos_keys = ['pan', 'tilt']
    precedence_mode = 'ltp'
    value_attribute = 'pos'
    value_keys = ['x', 'y']
    def __init__(self, **kwargs):
        self._pos = [0, 0]
        super(PositionGroup, self).__init__(**kwargs)
        
    def search_for_members(self, **kwargs):
        valid = False
        if self.device is not None:
            attribs = {}
            for attrib in self.device.Attributes.itervalues():
                if attrib.name.lower() in self.pos_keys:
                    self.add_member(**{attrib.name.lower():attrib})
            valid = len(self.members) >= 2
        return valid or super(PositionGroup, self).search_for_members(**kwargs)
    @property
    def _value(self):
        return self._pos
    @_value.setter
    def _value(self, value):
        self._pos = value
    @property
    def value(self):
        return self._pos
    @value.setter
    def value(self, value):
        self._set_pos(**dict(zip(self.pos_keys, value)))
    @property
    def value_seq(self):
        return self._pos
    @value_seq.setter
    def value_seq(self, value):
        self._set_pos(pan=value[0], tilt=value[1])
    @property
    def pan(self):
        return self._pos[0]
    @pan.setter
    def pan(self, value):
        self._set_pos(pan=value)
    @property
    def tilt(self):
        return self._pos[1]
    @tilt.setter
    def tilt(self, value):
        self._set_pos(tilt=value)
    @property
    def x(self):
        return self._pos[0]
    @x.setter
    def x(self, value):
        self._set_pos(x=value)
    @property
    def y(self):
        return self._pos[1]
    @y.setter
    def y(self, value):
        self._set_pos(y=value)
        
    @property
    def pos(self):
        keys = self.pos_keys + self.value_keys
        return dict(zip(keys, self._pos * 2))
    @pos.setter
    def pos(self, value):
        self._set_pos(**value)
        
    def _set_pos(self, **kwargs):
        for key, val in kwargs.iteritems():
            if key in self.pos_keys:
                keys = self.pos_keys
            elif key in self.value_keys:
                keys = self.value_keys
            i = keys.index(key)
            self._pos[i] = val
        self.set_attributes()
        self.on_value_change()
        self.emit('value_changed', value=self.value, attribute=self)
        
    def get_value_from_attributes(self):
        return [self.members[key].value for key in self.pos_keys]
        
    def set_attributes(self):
#        for x, val in enumerate(self._pos):
#            attrib = self.members.get(self.pos_keys[x])
#            if attrib:
#                self.member_values.update({attrib.id:val})
#                attrib.update_user_value(self)
        for key, val in self.members.iteritems():
            if isinstance(val, GroupBase):
                self.member_values.update({key:self._pos})
            elif key in self.pos_keys:
                self.member_values.update({key:self._pos[self.pos_keys.index(key)]})
            val.update_user_value(self)
            
    def on_member_value_changed(self, **kwargs):
        attrib = kwargs.get('attribute')
        value = kwargs.get('value')
        self._pos[self.pos_keys.index(attrib.name.lower())] = value
        
class ParameterGroup(AttributeGroup):
    _saved_class_name = 'ParameterGroup'
    signals_to_register = ['new_parameter', 'parameter_changed']
    def __init__(self, **kwargs):
        self._active_parameter = None
        self.indexed_parameters = {}
        self.parameter_obj = {}
        self.parameters_by_name = {}
        self.attribute_name = kwargs.get('attribute_name')
        if self.attribute_name:
            kwargs.setdefault('name', self.attribute_name)
            kwargs.setdefault('osc_address', self.attribute_name)
        super(ParameterGroup, self).__init__(**kwargs)
    @property
    def value(self):
        return self.active_parameter
    @value.setter
    def value(self, value):
        self.active_parameter = value
    @property
    def _value(self):
        return self._active_parameter
    @_value.setter
    def _value(self, value):
        self._active_parameter = value
    @property
    def active_parameter(self):
        return self._active_parameter
    @active_parameter.setter
    def active_parameter(self, value):
        if type(value) == list:
            if not len(value):
                return
            value = value[0]
        key = self.parameters_by_name.get(value)
        if key:
            for member in self.indexed_parameters[key].itervalues():
                member.active_parameter = value
        print 'group parameter:', value
                
    def get_value_from_attributes(self):
        return self.active_parameter
                
    def find_name_from_id(self, id=None):
        if id is None:
            id = self.active_parameter
        for key, val in self.parameters_by_name.iteritems():
            if val == id:
                return key
            
    def search_for_members(self, **kwargs):
        valid = False
        if self.device is not None:
            for key, val in self.device.Attributes.iteritems():
                if val.name.lower() == self.attribute_name.lower():
                    self.add_member(**{key:val})
            valid = len(self.indexed_parameters) > 0

        return valid or super(ParameterGroup, self).search_for_members(**kwargs)
        
    def add_member(self, **kwargs):
        super(ParameterGroup, self).add_member(**kwargs)
        if not self.membership_lock:
            for member in kwargs.itervalues():
                self.update_parameters(new_member=member)
                if self.device is not None:
                    member.connect('parameter_changed', self.on_member_parameter_changed)
                
    def del_member(self, key):
        if not self.membership_lock:
            self.members[key].disconnect(callback=self.on_member_parameter_changed)
            super(ParameterGroup, self).del_member(key)
            self.update_parameters(cleanup=True)
        
    def update_parameters(self, **kwargs):
        new_member = kwargs.get('new_member')
        cleanup = kwargs.get('cleanup', False)
        if new_member is not None:
            for pkey, pval in new_member.indexed_parameters.iteritems():
                if pkey not in self.indexed_parameters:
                    flag = True
                    for ukey, uval in self.members.iteritems():
                        if pkey not in uval.indexed_parameters:
                            flag = False
                    if flag:
                        self.indexed_parameters.update({pkey:{}})
                        if isinstance(new_member, ParameterGroup):
                            name = new_member.find_name_from_id(pkey)
                            obj = new_member.parameter_obj.get(name)
                            if obj and name not in self.parameter_obj:
                                self.parameter_obj.update({name:obj})
                        else:
                            name = pval.name
                            d = pval._get_saved_attr()
                            dparam = pval.DefinitionParameter
                            paramobj = Parameter.PatchedParameter(parent_attribute=pval.parent_attribute, 
                                                                  deserialize=d, DefinitionParameter=dparam)
                            self.parameter_obj.update({name:paramobj})
                        self.parameters_by_name.update({name:pkey})
                        self.emit('new_parameter', group=self, name=name, id=pkey)
                if pkey in self.indexed_parameters:
                    self.indexed_parameters[pkey].update({new_member.id:new_member})
        if cleanup:
            dead_keys = set()
            for key, val in self.indexed_parameters.iteritems():
                for member in self.members.itervalues():
                    if key not in member.indexed_parameters:
                        dead_keys.add(key)
            for key in dead_keys:
                name = None
                for nkey, nval in self.parameters_by_name.iteritems():
                    if nval == key:
                        name = nkey
                if name:
                    del self.parameters_by_name[name]
                del self.indexed_parameters[key]
        
    def on_member_parameter_changed(self, **kwargs):
        param = kwargs.get('parameter')
        if param.id in self.indexed_parameters and self._active_parameter != param.name:
            flag = True
            for key, val in self.indexed_parameters[param.id].iteritems():
                if val.active_parameter.id != param.id:
                    flag = False
            if flag:
                self._active_parameter = param.name
                self.emit('parameter_changed', group=self, name=param.name, id=param.id)

groupClasses = {}
for cls in [DimmerGroup, ColorGroup, PositionGroup]:
    groupClasses.update({cls._saved_class_name:cls})
